/* Copyright 2012 Tobias Marschall
 *
 * This file is part of CLEVER.
 *
 * CLEVER is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CLEVER is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CLEVER.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <sstream>
#include <iomanip>

#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/program_options.hpp>

#include "FastqReader.h"
#include "ShortDnaSequence.h"
#include "VersionInfo.h"

using namespace std;
namespace po = boost::program_options;
namespace io = boost::iostreams;

void usage(const char* name, const po::options_description& options_desc) {
	cerr << "Usage: " << name << " [options] <input.1.fastq(.gz)> <input.2.fastq(.gz)>" << endl;
	cerr << "       " << name << " [options] --single-end <input.fastq(.gz)>" << endl;
	cerr << endl;
	cerr << "Reads input in FASTQ format and writes prefix and suffix" << endl;
	cerr << "of each read to stdout." << endl;
	cerr << endl;
	cerr << options_desc << endl;
	exit(1);
}

bool split_read(ostream& os, const FastqReader::fastq_record_t& read, int nr, int split_length) {
	if ((int)read.sequence.size() < split_length) {
		ostringstream oss;
		oss << "Error: read \"" << read.name << "\" too short (" << read.sequence.size() << "bp)." << endl;
		throw std::runtime_error(oss.str());
	}
	// Write left end of read
	os << "@" << read.name << "_" << nr << "L" << endl;
	os << read.sequence.toString().substr(0, split_length) << endl;
	os << "+" << endl;
	os << read.sequence.qualityString().substr(0, split_length) << endl;
	// Write right end of read
	os << "@" << read.name << "_" << nr << "R" << endl;
	os << read.sequence.toString().substr(read.sequence.size()-split_length, split_length) << endl;
	os << "+" << endl;
	os << read.sequence.qualityString().substr(read.sequence.size()-split_length, split_length) << endl;
	return true;
}

int main(int argc, char* argv[]) {
	VersionInfo::checkAndPrintVersion("split-reads", cerr);
	string commandline = VersionInfo::commandline(argc, argv);

	// PARAMETERS
	int split_length = false;
	bool single_end = false;

	po::options_description options_desc("Allowed options");
	options_desc.add_options()
		("split_length,l", po::value<int>(&split_length)->default_value(35), "Length of prefix/suffix to be extracted.")
		("single-end", po::value<bool>(&single_end)->zero_tokens(), "Process single end reads (instead of paired end).")
	;

	for (int i=1; i<argc; ++i) {
		string arg(argv[i]);
		if (arg.compare("--single-end") == 0) single_end = true;
	}
	if ((single_end && (argc<2)) || (!single_end && (argc<3))) {
		usage(argv[0], options_desc);
	}
	string input1_filename = "";
	string input2_filename = "";
	if (single_end) {
		input1_filename = argv[argc-1];
		argc -= 1;
	} else {
		input1_filename = argv[argc-2];
		input2_filename = argv[argc-1];
		argc -= 2;
	}
	
	po::variables_map options;
	try {
		po::store(po::parse_command_line(argc, argv, options_desc), options);
		po::notify(options);
	} catch(exception& e) {
		cerr << "error: " << e.what() << "\n";
		return 1;
	}
	cerr << "Commandline: " << commandline << endl;

	ifstream* input1_istream = 0;
	io::filtering_istream* in1 = 0;
	FastqReader* fastq_reader1 = 0;
	ifstream* input2_istream = 0;
	io::filtering_istream* in2 = 0;
	FastqReader* fastq_reader2 = 0;
	
	input1_istream = new ifstream(input1_filename.c_str());
	if (input1_istream->fail()) {
		cerr << "Error opening file \"" + input1_filename + "\"." << endl;
		return 1;
	}
	in1 = new io::filtering_istream();
	if (input1_filename.substr(input1_filename.size()-3,3).compare(".gz") == 0) {
		in1->push(io::gzip_decompressor());
	}
	in1->push(*input1_istream);
	fastq_reader1 = new FastqReader(*in1, true);

	if (!single_end) {
		input2_istream = new ifstream(input2_filename.c_str());
		if (input2_istream->fail()) {
			cerr << "Error opening file \"" + input2_filename + "\"." << endl;
			return 1;
		}
		in2 = new io::filtering_istream();
		if (input2_filename.substr(input2_filename.size()-3,3).compare(".gz") == 0) {
			in2->push(io::gzip_decompressor());
		}
		in2->push(*input2_istream);
		fastq_reader2 = new FastqReader(*in2, true);
	}

	auto_ptr<FastqReader::fastq_record_t> read1(0);
	auto_ptr<FastqReader::fastq_record_t> read2(0);
	while (fastq_reader1->hasNext()) {
		if (!single_end) {
			if (!fastq_reader2->hasNext()) {
				cerr << "Error: unexpected end of input from \"" << input2_filename << "\"" << endl;
				return 1;
			}
		}
		read1 = fastq_reader1->getNext();
		if (!single_end) {
			read2 = fastq_reader2->getNext();
			if (read1->name.compare(read2->name) != 0) {
				cerr << "Error: input files out of sync." << endl;
				cerr << "       read name from \"" << input1_filename << "\": " << read1->name << endl;
				cerr << "       read name from \"" << input2_filename << "\": " << read2->name << endl;
				return 1;
			}
		}
		split_read(cout, *read1, 1, split_length);
		if (!single_end) {
			split_read(cout, *read2, 2, split_length);
		}
	}
	if (!single_end) {
		if (fastq_reader2->hasNext()) {
			cerr << "Error: unexpected end of input from \"" << input1_filename << "\"" << endl;
			return 1;
		}
	}
	return 0;
}
